package miggy.config;

import miggy.api.config.MachineDefinition;
import miggy.api.config.ConfigurationException;
import miggy.api.display.VideoMode;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Properties;
import java.util.logging.Logger;

/*
//  Miggy - Java Amiga MachineCore
//  Copyright (c) 2008, Tony Headford
//  All rights reserved.
//
//  Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
//  following conditions are met:
//
//    o  Redistributions of source code must retain the above copyright notice, this list of conditions and the
//       following disclaimer.
//    o  Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
//       following disclaimer in the documentation and/or other materials provided with the distribution.
//    o  Neither the name of the Miggy Project nor the names of its contributors may be used to endorse or promote
//       products derived from this software without specific prior written permission.
//
//  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
//  INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
//  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
//  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
//  SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
//  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
//  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// $Revision: 21 $
*/
public final class SystemDefinition implements MachineDefinition
{
	private static Logger logger = Logger.getLogger("Miggy.Config");

	public static final String KEY_MACHINE_NAME		= "machine.name";
	public static final String KEY_SYS_CLOCK		= "sys.clockspeed";
	public static final String KEY_DISPLAY_MODE		= "display.mode";
	public static final String KEY_CPU_CLASS		= "cpu.class";
	public static final String KEY_MEM_CLASS		= "mem.class";
	public static final String KEY_DISPLAY_CLASS	= "display.class";
	public static final String KEY_DMA_CLASS		= "dma.class";
	public static final String KEY_REG_CLASS		= "reg.class";
	public static final String KEY_MEM_CHIPRAM		= "mem.chipsize";
	public static final String KEY_MEM_SLOWRAM		= "mem.slowsize";
	public static final String KEY_MEM_FASTRAM		= "mem.fastsize";
	public static final String KEY_MEM_ROMFILE		= "mem.romfile";
	public static final String KEY_MEM_RTC			= "mem.rtc";

	private final Properties props;
	private String cpuClass;
	private String customRegsClass;
	private String memoryClass;
	private String dmaClass;
	private String displayClass;

	private String machineName;
	private int chipRam;
	private int slowRam;
	private int fastRam;
	private String romFile;
	private boolean hasRtc;
	private VideoMode videoMode;
	private int clockspeed;

	private SystemDefinition(Properties props)
	{
		this.props = props;
	}

	public static SystemDefinition load(String configFile) throws ConfigurationException
	{
		try
		{
			Properties p = new Properties();
			p.load(new FileInputStream(configFile));

			SystemDefinition config = new SystemDefinition(p);
			config.init();
			return config;
		}
		catch(FileNotFoundException e)
		{
			throw new ConfigurationException("Definition file [" + configFile + "] could not be found", e);
		}
		catch(IOException e)
		{
			throw new ConfigurationException("IO problem reading config file", e);
		}
	}

	private void init() throws ConfigurationException
	{
		//get the required params from the config
		//must have a name
		machineName = getString(KEY_MACHINE_NAME);
		logger.config("Machine Name: " + machineName);

		cpuClass = getString(KEY_CPU_CLASS);
		logger.config("CPU: " + cpuClass);

		memoryClass = getString(KEY_MEM_CLASS);
		logger.config("Memory Controller: " + memoryClass);

		customRegsClass = getString(KEY_REG_CLASS);
		logger.config("Custom Regs: " + customRegsClass);

		dmaClass = getString(KEY_DMA_CLASS);
		logger.config("DMA Controller: " + dmaClass);

		displayClass = getString(KEY_DISPLAY_CLASS);
		logger.config("Display Controller: " + displayClass);

		chipRam = getInt(KEY_MEM_CHIPRAM);
		logger.config("Chip Ram: " + chipRam);

		slowRam = getIntDefault(KEY_MEM_SLOWRAM, 0);
		logger.config("Slow Ram: " + slowRam);

		fastRam = getIntDefault(KEY_MEM_FASTRAM, 0);
		logger.config("Fast Ram: " + fastRam);

		romFile = getString(KEY_MEM_ROMFILE);
		logger.config("ROM file: " + romFile);

		hasRtc = getBoolDefault(KEY_MEM_RTC, false);
		logger.config("Has RTC: " + hasRtc);

		videoMode = VideoMode.valueOf(getString(KEY_DISPLAY_MODE));
		logger.config("Video mode: " + videoMode);

		clockspeed = getInt(KEY_SYS_CLOCK);
		logger.config("Clock speed: " + clockspeed);
	}


	public String getDefinitionName()
	{
		return machineName;
	}

	public String getRegisterControllerName()
	{
		return customRegsClass;
	}

	public String getCpuName()
	{
		return cpuClass;
	}

	public String getMemoryControllerName()
	{
		return memoryClass;
	}

	public String getDmaControllerName()
	{
		return dmaClass;
	}

	public String getDisplayControllerName()
	{
		return displayClass;
	}

	public int getChipRamSize()
	{
		return chipRam;
	}

	public int getSlowRamSize()
	{
		return slowRam;
	}

	public int getFastRamSize()
	{
		return fastRam;
	}

	public String getRomFilename()
	{
		return romFile;
	}

	public boolean hasRealTimeClock()
	{
		return hasRtc;
	}

	public VideoMode getVideoMode()
	{
		return videoMode;
	}

	public int getClockSpeed()
	{
		return clockspeed;
	}
	
	public String getString(String key) throws ConfigurationException
	{
		String val = props.getProperty(key);
		if(val == null)
			throw new ConfigurationException("Required value not configured: " + key);

		return val;
	}

	public int getInt(String key) throws ConfigurationException
	{
		int val;
		String s = props.getProperty(key);

		if(s != null)
		{
			try
			{
				val = Integer.valueOf(s);
			}
			catch(NumberFormatException e)
			{
				throw new ConfigurationException("Value not an integer for " + key);
			}
		}
		else
		{
			throw new ConfigurationException("Required value not configured: " + key);
		}
		return val;
	}

	public boolean getBool(String key) throws ConfigurationException
	{
		String s = props.getProperty(key);
		boolean val;

		if(s != null)
		{
			val = Boolean.valueOf(s);
		}
		else
		{
			throw new ConfigurationException("Required boolean value not configured: " + key);
		}

		return val;
	}

	public String getStringDefault(String key, String def)
	{
		String val = props.getProperty(key);
		if(val == null)
			return def;

		return val;
	}

	public int getIntDefault(String key, int def)
	{
		int val = 0;
		String s = props.getProperty(key);
		if(s != null)
		{
			try
			{
				val = Integer.valueOf(s);
			}
			catch(NumberFormatException e)
			{
				val = def;
			}
		}
		else
		{
			val = def;
		}

		return val;
	}

	public boolean getBoolDefault(String key, boolean def)
	{
		String s = props.getProperty(key);
		boolean val;

		if(s != null)
		{
			val = Boolean.valueOf(s);
		}
		else
		{
			val = def;
		}

		return val;
	}
}
